/**
 * \file Tree.cpp
 *
 * Asteriskos Compiler 1
 * Written 2010-2011, by Anthony Berlin and Alexander Carlqvist
 *
 * This file is part of ICC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Tree.h"

#include <GCB/Common.h>

///
/// \brief Constructors for \c Tree.
///
Tree::Tree(SourceLine* line, Token* token, TreeType type)
{
    init();

    m_type = type;
    m_line = line;
    m_token = token;
    m_parent = NULL;
    m_stack = new list<Tree*>;
}

Tree::Tree()
{
    init();
}

///
/// \brief Init some default values for data members.
///
inline void Tree::init()
{
    ++sValueTable.global_tree_ref;
    m_ref = sValueTable.global_tree_ref;

    m_datatype = NULL;
    m_line = NULL;
    m_token = NULL;
    m_parent = NULL;
    m_stack = NULL;
    m_type = (TreeType)0;
    m_rid = (TokenId)0;
    m_isLValue = false;
    m_isFunctionBody = false;
    m_isBinaryOperator = false;
    m_isOperator = false;
    m_isConstant = false;
}

///
/// \brief Clone this tree.
/// \return The cloned copy.
/// \note Only use this on a relatively small \c Tree.
///
Tree* Tree::clone(bool shallow)
{
    Tree* tree = new Tree(this->m_line, this->m_token, this->m_type);

    tree->m_parent = this->m_parent;
    tree->m_isFunctionBody = this->m_isFunctionBody;
    tree->m_isOperator = this->m_isOperator;
    tree->m_isBinaryOperator = this->m_isBinaryOperator;
    tree->m_isLValue = this->m_isLValue;
    tree->m_isSpecifier = this->m_isSpecifier;
    tree->m_isModifier = this->m_isModifier;
    tree->m_datatype = this->m_datatype->clone();
    tree->m_rid = this->m_rid;

    if (this->m_stack != NULL && !shallow)
    {
        foreach (Tree* sibling, *this->m_stack)
            if (sibling != NULL_TREE)
                tree->m_stack->push_back(sibling->clone());
    }

    return tree;
}

///
/// \brief Push a sibling \c Tree node onto the stack.
/// \param node The \c Tree to push.
///
void Tree::push(Tree* node, bool pushFront)
{
    if (node == NULL_TREE)
        return;

    if (pushFront)
        m_stack->push_front(node);
    else
        m_stack->push_back(node);

    if (node != this)
        node->m_parent = this;
}

///
/// \brief Pop a sibling from the stack.
/// \param node The \c Tree to pop.
///
void Tree::pop(Tree* node)
{
    if (node == NULL_TREE)
        return;

    m_stack->remove(node);
}

///
/// Return the first node of the stack (the one that got pushed onto the stack first).
///
Tree* Tree::front() const
{
    return m_stack->empty() ? NULL_TREE : m_stack->front();
}

///
/// Return the last node of the stack (the one that got pushed onto the stack last).
///
Tree* Tree::back() const
{
    return m_stack->empty() ? NULL_TREE : m_stack->back();
}

///
/// Return a node by index from the stack, counting from first added to last.
///
Tree* Tree::get_node(unsigned int index) const
{
    if (index < 1 || m_stack->empty())
        return NULL_TREE;

    unsigned int count = 0;

    foreach (Tree* tree, *m_stack)
    {
        ++count;

        if (count == index)
            return tree;
    }

    return m_stack->back();
}

///
/// Return a string representation of the tree type.
///
string Tree::convert_type_to_string() const
{
    string result;

    switch (m_type)
    {
        case TREE_NONE:
            result = "none";
            break;
        case TREE_DECLARATION_SEQ:
            result = "declaration-seq";
            break;
        case TREE_DATATYPE:
            result = "datatype";
            break;
        case TREE_PARAM_DECL_LIST:
            result = "param-decl-list";
            break;
        case TREE_PARAM_DECL:
            result = "param-decl";
            break;
        case TREE_VAR_DECL:
            result = "var-decl";
            break;
        case TREE_FUNC_DECL:
            result = "func-decl";
            break;
        case TREE_PARAM_TYPE_LIST:
            result = "param-type-list";
            break;
        case TREE_DECLARATOR:
            result = "declarator";
            break;
        case TREE_IDENT:
            result = "ident";
            break;
        case TREE_FUNC_DEF:
            result = "func-def";
            break;
        case TREE_ARITHM_EXPR:
            result = "arithm-expr";
            break;
        case TREE_EQUALITY_EXPR:
            result = "equality-expr";
            break;
        case TREE_RELATIONAL_EXPR:
            result = "relational-expr";
            break;
        case TREE_LOGICAL_AND_EXPR:
            result = "logical-and-expr";
            break;
        case TREE_LOGICAL_OR_EXPR:
            result = "logical-or-expr";
            break;
        case TREE_LOGICAL_NOT_EXPR:
            result = "logical-not-expr";
            break;
        case TREE_POLARITY_EXPR:
            result = "polarity-expr";
            break;
        case TREE_DECLARED_EXPR:
            result = "declared-expr";
            break;
        case TREE_ASSIGN_EXPR:
            result = "assign-expr";
            break;
        case TREE_CASTING_EXPR:
            result = "casting-expr";
            break;
        case TREE_PRIMARY_EXPR:
            result = "primary-expr";
            break;
        case TREE_ID_EXPR:
            result = "id-expression";
            break;
        case TREE_LITERAL:
            result = "literal";
            break;
        case TREE_FUNC_CALL:
            result = "func-call";
            break;
        case TREE_PARAM_LIST:
            result = "param-list";
            break;
        case TREE_CMP_STMT:
            result = "cmp-stmt";
            break;
        case TREE_RETURN_STMT:
            result = "return-stmt";
            break;
        case TREE_IF_STMT:
            result = "if-stmt";
            break;
        case TREE_ELSE_STMT:
            result = "else-stmt";
            break;
        case TREE_WHILE_STMT:
            result = "while-stmt";
            break;
        case TREE_PUT_STMT:
            result = "put-stmt";
            break;
        case TREE_GET_STMT:
            result = "get-stmt";
            break;
        case TREE_ASM_DECL:
            result = "asm-decl";
            break;
        case TREE_NULL_STMT:
            result = "null-stmt";
            break;
        case TREE_SPECIFIER_LIST:
            result = "specf-list";
            break;
        case TREE_SPECIFIER:
            result = "specifier";
            break;
        case TREE_MODIFIER_LIST:
            result = "modifier-list";
            break;
        case TREE_MODIFIER:
            result = "modifier";
            break;
        default:
            result = "unknown";
    }

    return result;
}

///
/// Return a string representation of the tree node.
///
string Tree::to_string() const
{
    string result;

    if (!m_token)
        result = convert_type_to_string();
    else if (m_token->m_lexeme != "")
        result = m_token->m_lexeme + "(" + convert_type_to_string() + ")";

    return result;
}

///
/// \brief Dump a text-representation of this tree to disc.
///
bool Tree::dump_to_disc(std::ofstream& fileStream) const
{
    if (!fileStream || !fileStream.is_open())
        return false;

    string outline = (string)"@" + boost::lexical_cast<string>(m_ref) + " = ";

    outline += (string)"<type : " + convert_type_to_string() + ">";

    if (m_parent && m_parent != NULL_TREE)
        outline += (string)", <parent : @" + boost::lexical_cast<string>(m_parent->m_ref) + ">";

    if (m_token && m_token->m_lexeme != "")
        outline += (string)", <toklex : \"" + m_token->m_lexeme + "\">";

    fileStream << outline << "\n";

    foreach (Tree* tree, *m_stack)
    {
        outline = (string)"     <sibling : @" + boost::lexical_cast<string>(tree->m_ref) + ">";
        fileStream << outline << "\n";
    }

    foreach (Tree* tree, *m_stack)
        if (!tree->dump_to_disc(fileStream))
            return false;

    return true;
}

///
/// \brief Set which symbol this tree is associated with (if any).
///
void Tree::set_associated_symbol(Symbol* symbol)
{
    if (symbol == NULL)
        return;

    m_symbol = symbol;
}
